﻿using System;
using System.Collections.ObjectModel;
using System.Xml;

namespace Snap.RssManager
{
	public class Manager : IDisposable
	{
		private string feedTitle;
		private string feedDescription;
		private readonly Collection<Rss.Item> rssItems;
		private bool isDisposed;

		#region Constructors

		public Manager() : this(string.Empty)
		{}

		public Manager(string feedUrl)
		{
			Url = feedUrl;
			FeedTitle = string.Empty;
			FeedDescription = string.Empty;
			rssItems = new Collection<Rss.Item>();
		}

		#endregion Constructors

		#region Properties

		/// <summary>
		/// Gets or sets the URL of the RSS feed to parse.
		/// </summary>
		/// <value>The URL.</value>
		public string Url { get; set; }

		/// <summary>
		/// Gets all the items in the RSS feed.
		/// </summary>
		/// <value>The RSS items.</value>
		public Collection<Rss.Item> RssItems
		{
			get { return rssItems; }
		}

		/// <summary>
		/// Gets the title of the RSS feed.
		/// </summary>
		/// <value>The feed title.</value>
		public string FeedTitle
		{
			get { return feedTitle; }
			private set { feedTitle = value; }
		}

		/// <summary>
		/// Gets the description of the RSS feed.
		/// </summary>
		/// <value>The feed description.</value>
		public string FeedDescription
		{
			get { return feedDescription; }
			private set { feedDescription = value; }
		}

		#endregion Properties

		public Collection<Rss.Item> GetFeed()
		{
			// Check to see if FeedUrl is empty
			if (String.IsNullOrEmpty( Url ))
			{
				throw new ArgumentException( "You must provide a feed URL." );
			}

			// Start the parsing process
			using (var reader = XmlReader.Create( Url ))
			{
				var xmlDocument = new XmlDocument();
				xmlDocument.Load( reader );
				// Parse the items of the feed
				ParseDocElements( xmlDocument.SelectSingleNode( "//channel" ), "title", ref feedTitle );
				ParseDocElements( xmlDocument.SelectSingleNode( "//channel" ), "description", ref feedDescription );
				ParseRssItems( xmlDocument );

				// Return the feed items
				return rssItems;
			}
		}

		private void ParseDocElements( XmlNode parent, string xPath, ref string property )
		{
			var node = parent.SelectSingleNode( xPath );
			if (null != node)
			{
				property = node.InnerText;
			}
			else
			{
				property = "Unresolvable";
			}
		}

		private void ParseRssItems( XmlDocument xmlDocument )
		{
			rssItems.Clear();
			var xmlNodes = xmlDocument.SelectNodes( "rss/channel/item" );

			if (null == xmlNodes) return;

			foreach (XmlNode xmlNode in xmlNodes)
			{
				var item = new Rss.Item();
				ParseDocElements( xmlNode, "title", ref item.Title );
				ParseDocElements( xmlNode, "description", ref item.Description );
				ParseDocElements( xmlNode, "link", ref item.Link );

				string date = null;
				ParseDocElements( xmlNode, "pubDate", ref date );
				DateTime.TryParse( date, out item.Date );

				rssItems.Add( item );
			}
		}

		private void Dispose( bool disposing )
		{
			if (disposing && !isDisposed)
			{
				rssItems.Clear();
				Url = null;
				FeedTitle = null;
				FeedDescription = null;
			}

			isDisposed = true;
		}

		#region IDisposable Members

		public void Dispose()
		{
			Dispose( true );
			GC.SuppressFinalize( this );
		}

		#endregion
	}
}
